package config

import (
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"io/ioutil"
	"os"
	"path"
)

var glConfig *AppConfig

type StorageConfig struct {
	Provider string `json:"provider"`
	Path     string `json:"path"`
}

type DebugConfig struct {
	Profile bool `json:"profile"`
}

type CloudSvrConfig struct {
	AliYunID  string `json:"aliyunid,omitempty"`
	AliYunKEY string `json:"aliyunkey,omitempty"`
}

type WebConfig struct {
	Port        uint16 `json:"port"`
	Tls         bool   `json:"tls,omitempty"`
	Gzip        bool   `json:"gzip,omitempty"`
	Certificate string `json:"certificate,omitempty"`
	CertKey     string `json:"cert_key,omitempty"`
}

type DatabaseConfig struct {
	Provider string `json:"provider"`
	Url      string `json:"url"`
	Debug    bool   `json:"debug"`
}

func (m *DatabaseConfig) GetUrl() string {
	out, err := aesDecrypt(m.Url)
	if err != nil {
		panic("aes decrypt failed.")
	}

	return out
}

func (m *DatabaseConfig) SetUrl(r string) (err error) {
	m.Url, err = aesEncrypt(r)
	if err != nil {
		fmt.Println("encrypt database url failed.", err)
	}
	return
}

type LogConfig struct {
	FileName string `json:"file_name"`
	Level    string `json:"level"`
}

type MQConfig struct {
	Provider string `json:"provider"`
	Path     string `json:"path"`
}

/*
AppConfig 应用程序配置中心
必填项为：
1，数据库配置
2，web 服务配置
3，日志配置
4，协议内容配置

Light，不参与序列化，协议配置，由 protocols 生成
*/
type AppConfig struct {
	Database *DatabaseConfig `json:"database"`
	Web      *WebConfig      `json:"web"`
	Logging  *LogConfig      `json:"logging"`
	CloudSvr *CloudSvrConfig `json:"cloud_svr,omitempty"`
	Debug    *DebugConfig    `json:"debug,omitempty"`
	Storage  *StorageConfig  `json:"storage,omitempty"`
	Mq       *MQConfig       `json:"mq,omitempty"`
}

// Show 配置打印
func (m *AppConfig) Show() {
	oldUrl := m.Database.Url
	m.Database.Url = m.Database.GetUrl()
	defer func() {
		m.Database.Url = oldUrl
	}()

	out, err := json.MarshalIndent(m, "", "    ")
	if err != nil {
		fmt.Println("json marshal failed.")
		return
	}
	fmt.Println(string(out))
}

func LoadConfig(filename string) (cfg *AppConfig, err error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}

	var appCfg AppConfig
	err = json.Unmarshal(data, &appCfg)
	if err != nil {
		return
	}

	// 检查必填项，首先是 数据库配置项
	if appCfg.Database == nil {
		fmt.Println("db config empty.")
		return nil, errors.New("missing databasse config")
	}

	dbCfg := appCfg.Database
	if dbCfg.Provider == "" || dbCfg.Url == "" {
		fmt.Println("db config provider or url empty.")
		return nil, errors.New("missing databasse config")
	}

	if appCfg.Web == nil {
		fmt.Println("web config empty.")
		return nil, errors.New("missing web config")
	}

	if appCfg.Logging == nil {
		fmt.Println("logging config empty.")
		return nil, errors.New("missing logging config")
	}

	if appCfg.Storage == nil {
		appCfg.Storage = &StorageConfig{
			Provider: "FILE",
			Path:     "./db",
		}
	}

	if appCfg.Debug == nil {
		appCfg.Debug = &DebugConfig{}
	}

	if appCfg.CloudSvr == nil {
		appCfg.CloudSvr = &CloudSvrConfig{}
	}

	cfg = &appCfg
	return
}

func SaveConfig(cfg *AppConfig, fname string) (err error) {
	data, err := json.MarshalIndent(cfg, "", "    ")
	if err != nil {
		fmt.Println("marshal config failed. ", err)
		return
	}

	return ioutil.WriteFile(fname, data, 0666)
}

/*
readConfig 按次序读取配置文件，读取成功则返回
配置文件排序，依次是
ROOT/config.json
ROOT/conf/config.json
*/
func ReadEnvConfig() (cfg *AppConfig, err error) {
	rootDir := os.Getenv("ESPUSH_ROOT")
	if rootDir == "" {
		fmt.Println("get espush root dir failed.")
		return nil, errors.New("ENV ESPUSH_ROOT empty.")
	}

	pathLists := []string{
		path.Join(rootDir, "config.json"),
		path.Join(rootDir, "conf/config.json"),
	}

	for _, item := range pathLists {
		cfg, err := LoadConfig(item)
		if err != nil {
			fmt.Printf("load config failed, at %s\n", item)
			continue
		}

		glConfig = cfg
		return cfg, nil
	}

	return nil, errors.New("read config file failed.")
}

func GlobalAppConfig() *AppConfig {
	return glConfig
}
